#ifndef _SUBDIV_H_
#define _SUBDIV_H_

#include "gl/glew.h"
#include "gl/glu.h"
#include "gl/glut.h"
#include "gl/GL.h"
#include "st.h"
#include "Vector.cpp"

#define MIN(a,b)((a)<(b))?(a):(b)
#define MAX(a,b)((a)>(b))?(a):(b)

class Edge;
class Face;
class Patch;

// minimal Wing Edge structure element
class Vertex 
{	
	Vector<Face*> faces;
	Vector<Edge*> edges;
	STVector3 vp;
public:
	Vertex(STVector3 v){pos = v;}
	~Vertex(){faces.FreeMemory(); edges.FreeMemory();}
	STVector3 pos;
	void addFace(Face* f){faces.PushEnd(f);}
	void addEdge(Edge* e){edges.PushEnd(e);}
	int valence(){return edges.Length();}
	void computeVertexPoint();
		void computeVertexPoint2();
	STVector3 getVertexPoint(){return vp;}
};

class Face 
{
	Vector<Edge*> edges;
	STVector3 fp;	//face point - average of all points on the face
	float BBSize;
	bool locked;	// When a face is small enough in screen space, it is locked.
public:
	Face(){locked = false;}
	~Face(){edges.FreeMemory();}
	bool isLocked(){return locked;}
	// float getBBSize(){return BBSize;}
	void setBBSize(float s);
	// void setLock(bool l){locked = l;}
	void addEdge(Edge* edge){edges.PushEnd(edge);}
	void computeFacePoint();	// this is ugly
	int getNumEdge(){return edges.Length();}
	STVector3 getFacePoint(){return fp;}
	bool hasVertex(Vertex* v);
	Edge* preEdge(int i);	// previous edge 
	Edge* getEdge(int i){return edges[i];}
};

// this is not bidirectional edge, no previous and next edge info.
class Edge 
{
	Face* lFace;
	Face *rFace;
	Vertex* vs;
	Vertex* ve;
	STVector3 ep; // edge point - avg. of two vertices and two sides face point
public:
	Edge(Vertex *s, Vertex *e){vs = s; ve = e; lFace = rFace = NULL;}
	void setRFace(Face* face){rFace = face;}
	void setLFace(Face* face){lFace = face;}
	Vertex* startV(Face* face);		// !!! has to check face
	Vertex* endV(Face* face);		// same
	bool reverseEqual(const STVector3 &s, const STVector3 &e);
	void computeEdgePoint();
	void computeEdgePoint2();
	STVector3 getEdgePoint(){return ep;}
	Face* getLFace(){return lFace;}
	Face* getRFace(){return rFace;}
	bool isBoundary(); 
};

// patch is a set of faces subdivided from same original face
class Patch
{
	Vector<Face*> faces;
public:
	Patch(){}
	// add face and check the bounding box to determine further subdivision
	void addFaces(Face* f);		
	Vector<Face*> getFaces(){return faces;}
	int numFaces(){return faces.Length();}
	int numFacesAfterSubdv();
};

class CatClarkSurface {
public:
	// Load your surface from an OBJ input file.
	CatClarkSurface(const char* filename);

	// Subdivide the surface several times
	void Subdivide(int iterations);

	void SetMVP(GLdouble* m, GLint* v, GLdouble* p){model=m;view=v;project=p;}

	// Draw the surface at the current level of subdivision
	void Draw() const;
	void DrawQuads() const;
	void DrawTriangles() const;

	// maintain the array strutrue for vbo useage
	void SetupVBOData(); 

	// Save out the subdivided surface
	void SaveOBJ(const char* filename) const;

	// Get bounding box of surface
	void GetBounds(STVector3& boundMin, STVector3& boundMax) const;
private:
	// Run your subdivision pipeline once
	void SubdivideOnce();
	void SubdivideOnce2();	// simple subdivision without modifying anything positions

	// Build any mesh data structures you need to
	void BuildMesh();

	// Compute the centroid of all faces in the mesh
	void ComputeFacePoints();

	// Compute the Catmull Clark edge points for each edge (the average
	// of the two edge endpoints and the two adjacent face points).
	void ComputeEdgePoints();
	void ComputeEdgePoints2();

	// Compute the new position of each original vertex. This value is
	// an averaging of the face points, the edge points and the original
	// vertex.
	void ComputeVertexPoints();
	void ComputeVertexPoints2();

	// Now that all the new positions are computed, compute the
	// corresponding topology.
	void ConnectVertices();				

	// face bounding box size
	float checkBBoxSize(Face* f);
	
	// Sentinel for unimplemented const STVector& functions.
	static const STVector3 kUnimplementedVector;
	// Return the number of faces in the mesh
	unsigned int NumFaces() const;
	// Return the number of patches in the mesh
	unsigned int NumPatches() const;
	// Return the number of vertices in the mesh
	unsigned int NumVertices() const;
	unsigned int NumIndices() const;
	// Get the number of vertices for a given face
	unsigned int NumVerticesInFace(unsigned int face) const;
	// Get the number of faces for a given patch
	unsigned int NumFacesInPatch(unsigned int face) const;
	// Get the index of the vth vertex of the given face
	unsigned int GetVertexIndex(unsigned int face, unsigned int v) const;
	// Get the vertex data for vertex i
	const STVector3& GetVertex(unsigned int i) const;
	// Get the vertex data for vertex v of the given face.
	const STVector3& GetVertexOfFace(unsigned int face, unsigned int v) const;

	// Raw input data
	Vector<STVector3> raw_vertices;					// vertex
	Vector<unsigned int> raw_indices;				// index
	Vector<unsigned int> raw_num_verts;			// face
	Vector<unsigned int> raw_num_faces;			// patch

	// output data for vbo, model in vbo are all triangles
	GLfloat* vbo_vertices;
	GLfloat* vbo_normals;
	GLfloat* vbo_colors;
	GLuint* vbo_indices;
	GLuint* vbo_patches;	// number of faces in each patch
	int num_vbo_patches;
	int num_vbo_indices;
	
public:
	GLfloat* GetVertexVBOData(){return vbo_vertices;} 
	GLfloat* GetNormalVBOData(){return vbo_normals;}
	GLfloat* GetColorVBOData(){return vbo_colors;}
	GLuint* GetIndexVBOData(){return vbo_indices;}
	GLuint* GetPatchVBOData(){return vbo_patches;}
	int GetNumVertex(){return raw_vertices.Length();}
	int GetNumIndex(){return num_vbo_indices;}
	int GetNumPathces(){return raw_num_faces.Length();}

private:
	// Wing Edge Mesh data 
	Vector<Vertex*> vertices;
	Vector<Edge*> edges;
	Vector<Patch*> patches;  	// faces are now inside patches

	// find the edge from edge list
	Edge* findEdge(const STVector3 &s, const STVector3 &e);
	Vertex* findVertex(STVector3 v);
	unsigned int insertVertex(STVector3 v);

	// mvp of OpenGL, used to calculate face projection 
	// global data in main file
	GLdouble* model;
	GLint* view;
	GLdouble* project;
};

#endif
